﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace Eniux.Windows.Controls.Panels
{
    /// <summary>
    /// 斜面边框
    /// </summary>
    public class BeveledBorder : Decorator
    {
        #region 私有变量

        private Rect borderRect;

        private Pen bottomPen;

        private Pen leftPen;

        private Pen rightPen;

        private Pen topPen;

        #endregion 私有变量

        #region 构造

        /// <summary>
        /// 静态构造
        /// </summary>
        static BeveledBorder()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(BeveledBorder), new FrameworkPropertyMetadata(typeof(BeveledBorder)));
        }

        #endregion 构造

        #region 注册属性

        #region Background

        public Brush Background
        {
            get { return (Brush)GetValue(BackgroundProperty); }
            set { SetValue(BackgroundProperty, value); }
        }

        public static readonly DependencyProperty BackgroundProperty =
            DependencyProperty.Register("Background", typeof(Brush), typeof(BeveledBorder), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender));

        #endregion Background

        #region Bevel

        public CornerRadius Bevel
        {
            get { return (CornerRadius)GetValue(BevelProperty); }
            set { SetValue(BevelProperty, value); }
        }

        public static readonly DependencyProperty BevelProperty =
            DependencyProperty.Register("Bevel", typeof(CornerRadius), typeof(BeveledBorder), new FrameworkPropertyMetadata(new CornerRadius(), FrameworkPropertyMetadataOptions.AffectsRender));

        #endregion Bevel

        #region BorderBrush

        public Brush BorderBrush
        {
            get { return (Brush)GetValue(BorderBrushProperty); }
            set { SetValue(BorderBrushProperty, value); }
        }

        public static readonly DependencyProperty BorderBrushProperty =
            DependencyProperty.Register("BorderBrush", typeof(Brush), typeof(BeveledBorder), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender, OnBorderBrushChanged));

        #endregion BorderBrush

        #region BorderThickness

        public Thickness BorderThickness
        {
            get { return (Thickness)GetValue(BorderThicknessProperty); }
            set { SetValue(BorderThicknessProperty, value); }
        }

        public static readonly DependencyProperty BorderThicknessProperty =
            DependencyProperty.Register("BorderThickness", typeof(Thickness), typeof(BeveledBorder), new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender, OnBorderThicknessChanged));

        #endregion BorderThickness

        #region Padding

        public Thickness Padding
        {
            get { return (Thickness)GetValue(PaddingProperty); }
            set { SetValue(PaddingProperty, value); }
        }

        public static readonly DependencyProperty PaddingProperty =
            DependencyProperty.Register("Padding", typeof(Thickness), typeof(BeveledBorder), new FrameworkPropertyMetadata(new Thickness(), FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender));

        #endregion Padding

        #endregion 注册属性

        #region 私有方法

        #region PropertyChanged方法

        private static void OnBorderBrushChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((BeveledBorder)obj).CachePens();
        }

        private static void OnBorderThicknessChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            ((BeveledBorder)obj).CachePens();
        }

        #endregion PropertyChanged方法

        #region 重写方法

        protected override Size MeasureOverride(Size constraint)
        {
            Size width = new Size();
            Size size = BeveledBorder.HelperCollapseThickness(this.BorderThickness);
            Size size1 = BeveledBorder.HelperCollapseThickness(this.Padding);
            Size size2 = new Size(size.Width + size1.Width, size.Height + size1.Height);
            if (Child == null)
            {
                return size2;
            }
            else
            {
                Size size3 = new Size(Math.Max(0, (double)(constraint.Width - size2.Width)), Math.Max(0, (double)(constraint.Height - size2.Height)));
                Child.Measure(size3);
                Size desiredSize = Child.DesiredSize;
                width.Width = desiredSize.Width + size2.Width;
                width.Height = desiredSize.Height + size2.Height;
                return width;
            }
        }

        protected override void OnRender(DrawingContext drawingContext)
        {
            List<Point> points = new List<Point>();
            List<Pen> pens = new List<Pen>();
            this.AddTopLeftPoints(points, pens, Bevel);
            this.AddTopRightPoints(points, pens, Bevel);
            this.AddBottomRightPoints(points, pens, Bevel);
            this.AddBottomLeftPoints(points, pens, Bevel);
            if (Background != null)
            {
                PathGeometry pathGeometry = new PathGeometry();
                PathFigure pathFigure = new PathFigure() { };
                pathFigure.StartPoint = points[0];
                for (int i = 1; i < points.Count; i++)
                {
                    pathFigure.Segments.Add(new LineSegment(points[i], true));
                }
                pathGeometry.Figures.Add(pathFigure);
                drawingContext.DrawGeometry(Background, null, pathGeometry);
            }

            if (BorderBrush != null && BorderThickness != null)
            {
                double thickness = BorderThickness.Left;
                if (thickness == BorderThickness.Left && thickness == BorderThickness.Right && thickness == BorderThickness.Top)
                {
                    PathGeometry pathGeometry = new PathGeometry();
                    PathFigure pathFigure = new PathFigure() { IsClosed = true };
                    pathFigure.StartPoint = points[0];
                    for (int i = 1; i < points.Count; i++)
                    {
                        pathFigure.Segments.Add(new LineSegment(points[i], true));
                    }
                    pathGeometry.Figures.Add(pathFigure);
                    drawingContext.DrawGeometry(null, GetPen(BorderThickness.Left, BorderBrush), pathGeometry);
                }
                else
                {
                    for (int j = 0; j < points.Count - 1; j++)
                    {
                        drawingContext.DrawLine(pens[j], points[j], points[j + 1]);
                        if (j == points.Count - 2)
                            drawingContext.DrawLine(this.leftPen, points[j + 1], points[0]);
                    }
                }
            }
        }

        #endregion 重写方法

        #region 其他方法

        private void AddBottomLeftPoints(List<Point> points, List<Pen> pens, CornerRadius bevel)
        {
            if (bevel.BottomLeft <= 0)
            {
                points.Add(this.borderRect.BottomLeft);
                pens.Add(this.bottomPen);
                return;
            }
            else
            {
                points.Add(new Point(this.borderRect.Left + bevel.BottomLeft, this.borderRect.Bottom));
                points.Add(new Point(this.borderRect.Left, this.borderRect.Bottom - bevel.BottomLeft));
                pens.Add(this.bottomPen);
                pens.Add(this.leftPen);
                return;
            }
        }

        private void AddBottomRightPoints(List<Point> points, List<Pen> pens, CornerRadius bevel)
        {
            if (bevel.BottomRight <= 0)
            {
                points.Add(this.borderRect.BottomRight);
                pens.Add(this.rightPen);
                return;
            }
            else
            {
                points.Add(new Point(this.borderRect.Right, this.borderRect.Bottom - bevel.BottomRight));
                points.Add(new Point(this.borderRect.Right - bevel.BottomRight, this.borderRect.Bottom));
                pens.Add(this.rightPen);
                pens.Add(this.rightPen);
                return;
            }
        }

        private void AddTopLeftPoints(List<Point> points, List<Pen> pens, CornerRadius bevel)
        {
            if (bevel.TopLeft <= 0)
            {
                points.Add(this.borderRect.TopLeft);
                return;
            }
            else
            {
                points.Add(new Point(this.borderRect.Left, this.borderRect.Top + bevel.TopLeft));
                points.Add(new Point(this.borderRect.Left + bevel.TopLeft, this.borderRect.Top));
                pens.Add(this.leftPen);
                return;
            }
        }

        private void AddTopRightPoints(List<Point> points, List<Pen> pens, CornerRadius bevel)
        {
            if (bevel.TopRight <= 0)
            {
                points.Add(this.borderRect.TopRight);
                pens.Add(this.topPen);
                return;
            }
            else
            {
                points.Add(new Point(this.borderRect.Right - bevel.TopRight, this.borderRect.Top));
                points.Add(new Point(this.borderRect.Right, this.borderRect.Top + bevel.TopRight));
                pens.Add(this.topPen);
                pens.Add(this.rightPen);
                return;
            }
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            UIElement child = this.Child;
            if (child != null)
            {
                Rect rect = BeveledBorder.HelperDeflateRect(new Rect(finalSize), this.BorderThickness);
                Rect rect1 = BeveledBorder.HelperDeflateRect(rect, this.Padding);
                child.Arrange(rect1);
            }
            Thickness borderThickness = this.BorderThickness;
            Thickness thickness = this.BorderThickness;
            this.borderRect = new Rect(borderThickness.Left / 2, thickness.Top / 2, finalSize.Width, finalSize.Height);
            return finalSize;
        }

        private void CachePens()
        {
            this.leftPen = GetPen(BorderThickness.Left, BorderBrush);
            this.rightPen = GetPen(BorderThickness.Right, BorderBrush);
            this.topPen = GetPen(BorderThickness.Top, BorderBrush);
            this.bottomPen = GetPen(BorderThickness.Bottom, BorderBrush);
        }

        private Pen GetPen(double thickness, Brush brush)
        {
            Pen pen = new Pen() { Brush = brush, Thickness = thickness };
            if (brush != null && brush.IsFrozen)
            {
                pen.Freeze();
            }
            return pen;
        }

        private static Size HelperCollapseThickness(Thickness th)
        {
            return new Size(th.Left + th.Right, th.Top + th.Bottom);
        }

        private static Rect HelperDeflateRect(Rect rt, Thickness thick)
        {
            return new Rect(rt.Left + thick.Left, rt.Top + thick.Top, Math.Max(0, (double)(rt.Width - thick.Left - thick.Right)), Math.Max(0, (double)(rt.Height - thick.Top - thick.Bottom)));
        }

        #endregion 其他方法

        #endregion 私有方法
    }
}